hvmloader: Add new test for MSR_SHADOW_GS_BASE validity after SWAPGS instruction.
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 16 Jul 2009 09:30:50 +0000 (10:30 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 16 Jul 2009 09:30:50 +0000 (10:30 +0100)
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
tools/firmware/hvmloader/hvmloader.c
tools/firmware/hvmloader/hypercall.h
tools/firmware/hvmloader/tests.c
tools/firmware/hvmloader/util.h

index e42f0f74d143c203a8f7eef6d30fb2a8e9bfc565..c154eff952a020ec55b75d88faccad2cf2fc3f28 100644 (file)
@@ -22,8 +22,8 @@
 
 #include "roms.h"
 #include "acpi/acpi2_0.h"
-#include "hypercall.h"
 #include "util.h"
+#include "hypercall.h"
 #include "config.h"
 #include "apic_regs.h"
 #include "pci_regs.h"
@@ -40,7 +40,15 @@ asm (
     /* C runtime kickoff. */
     "    cld                         \n"
     "    cli                         \n"
-    "    movl $stack_top,%esp        \n"
+    "    lgdt gdt_desr               \n"
+    "    mov  $"STR(SEL_DATA32)",%ax \n"
+    "    mov  %ax,%ds                \n"
+    "    mov  %ax,%es                \n"
+    "    mov  %ax,%fs                \n"
+    "    mov  %ax,%gs                \n"
+    "    mov  %ax,%ss                \n"
+    "    ljmp $"STR(SEL_CODE32)",$1f \n"
+    "1:  movl $stack_top,%esp        \n"
     "    movl %esp,%ebp              \n"
     "    call main                   \n"
     /* Relocate real-mode trampoline to 0x0. */
@@ -50,8 +58,7 @@ asm (
     "    sub  %esi,%ecx              \n"
     "    rep  movsb                  \n"
     /* Load real-mode compatible segment state (base 0x0000, limit 0xffff). */
-    "    lgdt gdt_desr               \n"
-    "    mov  $0x0010,%ax            \n"
+    "    mov  $"STR(SEL_DATA16)",%ax \n"
     "    mov  %ax,%ds                \n"
     "    mov  %ax,%es                \n"
     "    mov  %ax,%fs                \n"
@@ -67,7 +74,7 @@ asm (
     "    xor  %esi,%esi              \n"
     "    xor  %edi,%edi              \n"
     /* Enter real mode, reload all segment registers and IDT. */
-    "    ljmp $0x8,$0x0              \n"
+    "    ljmp $"STR(SEL_CODE16)",$0x0\n"
     "trampoline_start: .code16       \n"
     "    mov  %eax,%cr0              \n"
     "    ljmp $0,$1f-trampoline_start\n"
@@ -90,6 +97,9 @@ asm (
     "    .quad 0x0000000000000000    \n"
     "    .quad 0x008f9a000000ffff    \n" /* Ring 0 16b code, base 0 limit 4G */
     "    .quad 0x008f92000000ffff    \n" /* Ring 0 16b data, base 0 limit 4G */
+    "    .quad 0x00cf9a000000ffff    \n" /* Ring 0 32b code, base 0 limit 4G */
+    "    .quad 0x00cf92000000ffff    \n" /* Ring 0 32b data, base 0 limit 4G */
+    "    .quad 0x00af9a000000ffff    \n" /* Ring 0 64b code */
     "gdt_end:                        \n"
     "                                \n"
     "    .bss                        \n"
index 4bd2707fe7de0eb64da9943f904e823fb913ddcf..2df7247359986dd22e10923e37c44a762ecbf14e 100644 (file)
@@ -35,9 +35,6 @@
 #include <xen/xen.h>
 #include "config.h"
 
-#define __STR(...) #__VA_ARGS__
-#define STR(...) __STR(__VA_ARGS__)
-
 /*
  * NB. Hypercall address needs to be relative to a linkage symbol for
  * some version of ld to relocate the relative calls properly.
index d3ee9cfa98d9a81c37805613c895bb29a8aec0a1..f6f2f269e5ed30bb995527be8a5f5113e135d058 100644 (file)
 
 #include "util.h"
 
+#define TEST_FAIL 0
+#define TEST_PASS 1
+#define TEST_SKIP 2
+
 /*
  * Memory layout during tests:
  *  4MB to 8MB is cleared.
@@ -73,7 +77,7 @@ static int rep_io_test(void)
 {
     uint32_t *p;
     uint32_t i, p0, p1, p2;
-    int okay = 1;
+    int okay = TEST_PASS;
 
     static const struct {
         unsigned long addr;
@@ -121,44 +125,114 @@ static int rep_io_test(void)
         {
             printf("Bad value at 0x%08lx: saw %08x expected %08x\n",
                    (unsigned long)p, *p, expected);
-            okay = 0;
+            okay = TEST_FAIL;
         }
     }
 
     return okay;
 }
 
+static int shadow_gs_test(void)
+{
+    uint64_t *pd = (uint64_t *)PD_START;
+    uint32_t i, eax, ebx, ecx, edx;
+
+    /* Skip this test if the CPU does not support long mode. */
+    cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
+    if ( eax < 0x80000001 )
+        return TEST_SKIP;
+    cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
+    if ( !(edx & (1u<<29)) )
+        return TEST_SKIP;
+
+    /* Long mode pagetable setup: Identity map 0-16MB with 2MB mappings. */
+    *pd = (unsigned long)pd + 0x1007; /* Level 4 */
+    pd += 512;
+    *pd = (unsigned long)pd + 0x1007; /* Level 3 */
+    pd += 512;
+    for ( i = 0; i < 8; i++ )         /* Level 2 */
+        *pd++ = (i << 21) + 0x1e3;
+
+    asm volatile (
+        /* CR4.PAE=1 */
+        "mov $0x20,%%ebx; "
+        "mov %%ebx,%%cr4; "
+        /* CR3 */
+        "mov %%eax,%%cr3; "
+        /* EFER.LME=1 */
+        "mov $0xc0000080,%%ecx; rdmsr; btsl $8,%%eax; wrmsr; "
+        /* CR0.PG=1 */
+        "mov %%cr0,%%eax; btsl $31,%%eax; mov %%eax,%%cr0; "
+        "jmp 1f; 1: "
+        /* GS_BASE=2; SHADOW_GS_BASE=3 */
+        "mov $0xc0000101,%%ecx; xor %%edx,%%edx; mov $2,%%eax; wrmsr; "
+        "mov $0xc0000102,%%ecx; xor %%edx,%%edx; mov $3,%%eax; wrmsr; "
+        /* Push LRETQ stack frame. */
+        "pushl $0; pushl $"STR(SEL_CODE32)"; pushl $0; pushl $2f; "
+        /* Jump to 64-bit mode. */
+        "ljmp $"STR(SEL_CODE64)",$1f; 1: "
+        /* Swap GS_BASE and SHADOW_GS_BASE */
+        ".byte 0x0f,0x01,0xf8; " /* SWAPGS */
+        /* Jump to 32-bit mode. */
+        ".byte 0x89, 0xe4; "     /* MOV ESP,ESP */
+        ".byte 0x48, 0xcb; 2: "  /* LRETQ */
+        /* Read SHADOW_GS_BASE: should now contain 2 */
+        "mov $0xc0000102,%%ecx; rdmsr; mov %%eax,%%ebx; "
+        /* CR0.PG=0 */
+        "mov %%cr0,%%eax; btcl $31,%%eax; mov %%eax,%%cr0; "
+        "jmp 1f; 1:"
+        /* EFER.LME=0 */
+        "mov $0xc0000080,%%ecx; rdmsr; btcl $8,%%eax; wrmsr; "
+        /* CR4.PAE=0 */
+        "xor %%eax,%%eax; mov %%eax,%%cr4; "
+        : "=b" (ebx) : "a" (PD_START) : "ecx", "edx", "memory" );
+
+    return (ebx == 2) ? TEST_PASS : TEST_FAIL;
+}
+
 void perform_tests(void)
 {
-    int i, passed;
+    int i, passed, skipped;
 
     static struct {
         int (* const test)(void);
         const char *description;
     } tests[] = {
         { rep_io_test, "REP INSB across page boundaries" },
+        { shadow_gs_test, "GS base MSRs and SWAPGS" },
         { NULL, NULL }
     };
 
     printf("Testing HVM environment:\n");
 
-    passed = 0;
+    passed = skipped = 0;
     for ( i = 0; tests[i].test; i++ )
     {
         printf(" - %s ... ", tests[i].description);
         memset((char *)(4ul << 20), 0, 4ul << 20);
         setup_paging();
-        if ( (*tests[i].test)() )
+        switch ( (*tests[i].test)() )
         {
+        case TEST_PASS:
             printf("passed\n");
             passed++;
-        }
-        else
-        {
+            break;
+        case TEST_FAIL:
             printf("failed\n");
+            break;
+        case TEST_SKIP:
+            printf("skipped\n");
+            skipped++;
+            break;
         }
     }
 
-    printf("Passed %d/%d tests\n", passed, i);
-    BUG_ON(passed != i);
+    printf("Passed %d of %d tests\n", passed, i);
+    if ( skipped != 0 )
+        printf("Skipped %d of %d tests\n", skipped, i);
+    if ( (passed + skipped) != i )
+    {
+        printf("FAILED %d of %d tests\n", i - passed - skipped, i);
+        BUG();
+    }
 }
index e9e0dfd0729aa0a81ba7aef117475fd0d02d5f3c..c12a47f6c211bc0b18ae2b09e8bf0d8fe34faa25 100644 (file)
@@ -5,6 +5,16 @@
 #include <stdint.h>
 #include <xen/hvm/hvm_info_table.h>
 
+#define __STR(...) #__VA_ARGS__
+#define STR(...) __STR(__VA_ARGS__)
+
+/* GDT selector values. */
+#define SEL_CODE16          0x0008
+#define SEL_DATA16          0x0010
+#define SEL_CODE32          0x0018
+#define SEL_DATA32          0x0020
+#define SEL_CODE64          0x0028
+
 #undef offsetof
 #define offsetof(t, m) ((unsigned long)&((t *)0)->m)